package mbus

import (
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	hardware "gitee.com/wj008/hardware"
	"gitee.com/wj008/hardware/libs"
	"github.com/goburrow/serial"
	"github.com/things-go/go-modbus"
	log "github.com/wj008/goyee/logger"
	"strings"
	"sync"
	"time"
)

type Device struct {
	sync.Mutex
	Id       int
	Kind     string
	Number   string
	Conn     modbus.Client
	Addr     string
	Slave    uint8
	Rate     int
	DataBits int
	StopBits int
	Parity   string
	Endian   string
	//频率
	Freq       int
	Watch      string
	WriteEn    int
	IsRun      bool //设备是否正常运行
	LastTime   int  //上次提交时间按
	chSend     chan *libs.WItem
	chSendOpen bool
	PutItems   func(d hardware.IDevice, data map[string]any)
}

func (d *Device) GetId() int {
	return d.Id
}
func (d *Device) GetNum() string {
	return d.Number
}
func (d *Device) GetKind() string {
	return d.Kind
}

func (d *Device) GetWatch() []string {
	result := make([]string, 0)
	items := strings.Split(d.Watch, ",")
	for _, item := range items {
		item = strings.TrimSpace(item)
		if item == "" {
			continue
		}
		parts := strings.SplitN(item, ":", 2)
		if len(parts) != 2 {
			continue
		}
		addr := strings.TrimSpace(parts[0])
		result = append(result, addr)
	}
	return result
}

func (d *Device) KeepRun(ctx context.Context) {
	timer := 1
	for {
		select {
		case <-ctx.Done():
			log.LPrintf(2, "MBUS# Id:%d:服务被终止....\n", d.Id)
			return
		case <-time.After(time.Duration(timer) * time.Second):
			timer = 5
			log.LPrintf(2, "MBUS# Id:%d:服务被重新启动....\n", d.Id)
			_ = d.Setup(ctx)
		}
	}
}

func (d *Device) Stop() {
	d.Lock()
	defer d.Unlock()
	d.IsRun = false
	log.LPrintln(2, "MBUS#关闭服务")
	if d.Conn != nil {
		_ = d.Conn.Close()
	}
	if d.chSendOpen {
		close(d.chSend)
		d.chSendOpen = false
	}
}

func (d *Device) Setup(parentCtx context.Context) (err error) {
	d.Lock()
	d.chSend = make(chan *libs.WItem, 10)
	d.chSendOpen = true
	d.Unlock()
	ctx, cancel := context.WithCancel(parentCtx)
	defer cancel() // 确保退出时取消子上下文
	if d.Kind == "modbus_rtu" {
		cfg := serial.Config{
			Address:  d.Addr,
			BaudRate: d.Rate,
			DataBits: d.DataBits,
			StopBits: d.StopBits,
			Parity:   d.Parity,
			Timeout:  modbus.SerialDefaultTimeout,
		}
		p := modbus.NewRTUClientProvider(func(p modbus.ClientProvider) {}, modbus.WithSerialConfig(cfg))
		d.Conn = modbus.NewClient(p)
	} else {
		p := modbus.NewTCPClientProvider(d.Addr, modbus.WithTCPTimeout(time.Second*200))
		d.Conn = modbus.NewClient(p)
	}
	err = d.Conn.Connect()
	if err != nil {
		return err
	}
	d.IsRun = true
	log.LPrintf(2, "MBUS# Id:%d:链接成功\n", d.Id)
	errChan := make(chan error, 2)
	// 启动读取协程
	go func() {
		err := d.readFromPort(ctx)
		if err != nil {
			errChan <- fmt.Errorf("读取协程错误: %w", err)
		}
	}()
	go func() {
		err := d.processWrites(ctx)
		if err != nil {
			errChan <- fmt.Errorf("写入协程错误: %w", err)
		}
	}()
	select {
	case <-ctx.Done():
		d.Stop()
		return ctx.Err()
	case err2 := <-errChan:
		log.LPrintln(2, err2.Error())
		d.Stop()
		return err2
	}
}

func (d *Device) readFromPort(ctx context.Context) (err error) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(time.Duration(d.Freq) * time.Second):
			if d.Conn == nil || !d.Conn.IsConnected() {
				return fmt.Errorf("MBUS# Id:%d:链接已经丢失\n", d.Id)
			}
			err = d.CatchData()
			if err != nil {
				log.LPrintln(2, d.Id, err.Error())
				return err
			}
		}
	}
}

// processWrites 处理发送数据，发生错误时返回
func (d *Device) processWrites(ctx context.Context) error {
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case data := <-d.chSend:
			if err := d.write(data); err != nil {
				// 返回写入错误，会被setup函数捕获
				return fmt.Errorf("写入数据失败: %w", err)
			}
		}
	}
}

func (d *Device) write(item *libs.WItem) (err error) {
	if d.Conn == nil || !d.Conn.IsConnected() {
		return fmt.Errorf("MBS# Id:%d:链接意外退出\n", d.Id)
	}
	if 1 != d.WriteEn {
		return fmt.Errorf("MBS# Id:%d:设备禁止写入任何数据\n", d.Id)
	}
	code := item.Addr
	typ := strings.ToUpper(item.Typ)
	value := item.Value
	tag, err := ParseTag(code, typ)
	if err != nil {
		return
	}
	if tag.Area == "C" && tag.Typ == "BOOL" {
		if tag.Typ == "BOOL" {
			return d.Conn.WriteSingleCoil(d.Slave, uint16(tag.Start), value.(bool))
		} else {
			return fmt.Errorf("MBS# Id:%d:[C]寄存器只能写入bool类型数据\n", d.Id)
		}
	}
	if tag.Area != "H" {
		return fmt.Errorf("MBS# Id:%d:[%s]寄存器不支持写入数据\n", d.Id, tag.Area)
	}
	//读取 holding
	var buffer []byte
	switch tag.Typ {
	case "BOOL":
		if tag.MBit > 15 || tag.MBit < 0 {
			return fmt.Errorf("MBS# Id:%d:读取数据Bit无效\n", d.Id)
		}
		orMask := uint16(1 << tag.MBit)
		andMask := ^orMask
		if !value.(bool) {
			orMask = 0
		}
		//需要使用掩码读写
		return d.Conn.MaskWriteRegister(d.Slave, uint16(tag.Start), andMask, orMask)
	case "I16":
		buffer = make([]byte, 2)
		err = libs.SetValueAt(buffer, 0, value.(int16))
		break
	case "U16":
		buffer = make([]byte, 2)
		err = libs.SetValueAt(buffer, 0, value.(uint16))
		break
	case "I32":
		buffer = make([]byte, 4)
		err = libs.SetValueAt(buffer, 0, value.(int32))
		break
	case "U32":
		buffer = make([]byte, 4)
		err = libs.SetValueAt(buffer, 0, value.(uint32))
		break
	case "FLOAT":
		buffer = make([]byte, 4)
		switch value.(type) {
		case float64:
			err = libs.SetRealAt(buffer, 0, float32(value.(float64)))
			break
		case float32:
			err = libs.SetRealAt(buffer, 0, value.(float32))
			break
		default:
			err = errors.New("error type.")
			break
		}
		break
	}
	if err != nil {
		return err
	}
	sizeLen := len(buffer)
	if sizeLen == 0 {
		return fmt.Errorf("MBS# Id:%d:写入数据失败\n", d.Id)
	} else if sizeLen == 2 {
		uVal := binary.BigEndian.Uint16(buffer)
		//log.LPrintln(2,buffer, uVal)
		return d.Conn.WriteSingleRegister(d.Slave, uint16(tag.Start), uVal)
	} else {
		return d.Conn.WriteMultipleRegistersBytes(d.Slave, uint16(tag.Start), uint16(tag.Quantity), buffer)
	}
}

func (d *Device) LoadMaps() (libs.RMap, error) {
	result := make(libs.RMap)
	items := strings.Split(d.Watch, ",")
	for _, item := range items {
		r := libs.ToRItem(item)
		if r == nil {
			continue
		}
		result[r.Addr] = r
	}
	return result, nil
}

func (d *Device) CatchData() (err error) {
	if d.Conn == nil || !d.Conn.IsConnected() {
		return fmt.Errorf("MBUS# Id:%d:链接意外退出\n", d.Id)
	}
	if d.PutItems == nil {
		return nil
	}
	tagMaps, err := d.LoadMaps()
	if err != nil {
		return fmt.Errorf("MBUS# Id:%d:没有配置抓取字典\n", d.Id)
	}
	if tagMaps == nil {
		return fmt.Errorf("MBUS# Id:%d:没有配置抓取字典\n", d.Id)
	}
	//err = d.SingleWrite("H0", "U16", uint16(255))
	dataMap, err := d.MultiRead(tagMaps)
	if err != nil {
		log.LPrintln(2, "err:", err)
		return err
	}
	d.PutItems(d, dataMap)
	return nil
}

// SingleRead 简单读取
func (d *Device) SingleRead(code, typ string) (value any, err error) {
	d.Lock()
	defer d.Unlock()
	if d.Conn == nil || !d.Conn.IsConnected() {
		return nil, fmt.Errorf("MBUS# Id:%d:链接意外退出\n", d.Id)
	}
	tag, err := ParseTag(code, typ)
	if err != nil {
		return
	}
	//	log.LPrintln(2,tag.Code, tag.Addr, tag.Area, tag.Typ, tag.Start, tag.MBit, tag.Quantity)
	if (tag.Area == "C" || tag.Area == "D") && tag.Typ != "BOOL" {
		err = fmt.Errorf("MBUS# Id:%d:[%s]寄存器只能读取BOOL类型数据", d.Id, tag.Area)
		return
	}
	var buffer []byte
	if tag.Area == "H" {
		buffer, err = d.Conn.ReadHoldingRegistersBytes(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
	} else if tag.Area == "C" {
		buffer, err = d.Conn.ReadCoils(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
	} else if tag.Area == "D" {
		buffer, err = d.Conn.ReadDiscreteInputs(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
	} else if tag.Area == "I" {
		buffer, err = d.Conn.ReadInputRegistersBytes(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
	}
	if err != nil {
		return nil, err
	}
	if buffer == nil || len(buffer) == 0 {
		return nil, fmt.Errorf("MBUS# Id:%d:读取数据失败\n", d.Id)
	}
	value, err = ReadTag(buffer, 0, tag, d.Endian)
	return
}

// SingleWrite 简单写入
func (d *Device) SingleWrite(code, typ string, value any) (err error) {
	d.Lock()
	defer d.Unlock()
	if !d.IsRun || !d.chSendOpen {
		return fmt.Errorf("MBUS# Id:%d:服务未运行\n", d.Id)
	}
	item := &libs.WItem{
		Addr:  code,
		Typ:   typ,
		Value: value,
	}
	select {
	case d.chSend <- item:
		return nil
	case <-time.After(1 * time.Second):
		return errors.New("发送数据超时")
	}
}

// MultiRead 批量标签读取
func (d *Device) MultiRead(maps libs.RMap) (valueMap map[string]any, err error) {
	d.Lock()
	defer d.Unlock()
	if d.Conn == nil || !d.Conn.IsConnected() {
		return nil, fmt.Errorf("MBUS# Id:%d:链接意外退出\n", d.Id)
	}
	tc := NewTagBucket()
	for code, item := range maps {
		item.Typ = strings.ToUpper(item.Typ)
		item.Addr = strings.ToUpper(item.Addr)
		tag, err2 := ParseTag(item.Addr, item.Typ)
		if err2 != nil {
			return nil, fmt.Errorf("MBUS# Id:%d:[%s][%s]解析有误,无法读取\n", d.Id, item.Addr, item.Typ)
		}
		tag.Code = code
		//log.LPrintln(2,"TAG:", code, tag.Area, tag.Code, tag.Addr, tag.Typ)
		if !ValidType(tag.Typ) {
			continue
		}
		tc.Append(tag)
	}
	return d.tagsRead(tc)
}

// tagsRead 批量标签读取
func (d *Device) tagsRead(tc *TagBucket) (valueMap map[string]any, err error) {
	if d.Conn == nil || !d.Conn.IsConnected() {
		return nil, fmt.Errorf("MBUS# Id:%d:链接意外退出\n", d.Id)
	}
	valueMap = make(map[string]any)
	for _, group := range tc.Groups {
		for _, tag := range group.Elems {
			var buffer []byte
			if tag.Area == "H" {
				buffer, err = d.Conn.ReadHoldingRegistersBytes(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
			} else if tag.Area == "C" {
				buffer, err = d.Conn.ReadCoils(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
			} else if tag.Area == "D" {
				buffer, err = d.Conn.ReadDiscreteInputs(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
			} else if tag.Area == "I" {
				buffer, err = d.Conn.ReadInputRegistersBytes(d.Slave, uint16(tag.Start), uint16(tag.Quantity))
			}
			if err != nil {
				return nil, fmt.Errorf("MBUS# Id:%d:读取[%s][%s]失败,Err:%s\n", d.Id, tag.Addr, tag.Typ, err.Error())
			}
			if buffer == nil || len(buffer) == 0 {
				return nil, fmt.Errorf("MBUS# Id:%d:读取[%s][%s]失败,无法读取\n", d.Id, tag.Addr, tag.Typ)
			}
			value, readErr := ReadTag(buffer, 0, tag, d.Endian)
			if readErr != nil {
				continue
			}
			valueMap[tag.Code] = value
		}
	}
	return valueMap, nil
}
